import http from 'http';
import * as url from 'url';
import { RequestHeaders, RequestBody, Method } from './exchange';

export type AttrValue = object | number | string | boolean | null;

export interface Request {
  readonly headers: RequestHeaders;
  readonly method: Method;
  readonly version: string;
  readonly url: string;
  readonly remoteAddress: string;
  readonly remoteFamily: string;
  readonly remotePort: number;
  readonly path: string | null;
  readonly querystring: string | null;
  readonly req: http.IncomingMessage;
  readonly type: string | null;
  body: RequestBody;

  /**
   * @param name attr 名称
   * @return 如果名为 `name` 的 attr 不存在，返回 `undefined`
   */
  getAttr(name: string): AttrValue | undefined;

  /**
   * @param name attr 名称
   * @param value attr 值，不接受 `undefined`
   */
  setAttr(name: string, value: AttrValue): void;

  /**
   * @param name attr 名称
   * @returns attr 的值。如果名为 `name` 的 attr 不存在，则返回 `undefined`
   */
  removeAttr(name: string): AttrValue | undefined;
  // TODO: query
  // TODO: anchor
}

export class HttpRequest implements Request {
  #req: http.IncomingMessage;
  #url: string;
  #path: string | null;
  #querystring: string | null;
  #body: RequestBody;
  #attrs: Map<string, AttrValue> = new Map();
  constructor(req: http.IncomingMessage) {
    this.#req = req;
    this.#url = req.url || '/';
    const urlObj = url.parse(this.#url);
    this.#path = urlObj.pathname;
    this.#querystring = urlObj.query;
  }

  get body(): RequestBody {
    return this.#body;
  }

  set body(v: RequestBody) {
    this.#body = v;
  }

  get headers(): RequestHeaders {
    return this.#req.headers;
  }

  get method(): Method {
    return this.#req.method as Method;
  }

  get version(): string {
    return this.#req.httpVersion;
  }

  get url(): string {
    return this.#req.url || '';
  }

  get path(): string | null {
    return this.#path;
  }

  get querystring(): string | null {
    return this.#querystring;
  }

  get remoteAddress(): string {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    return this.#req.socket.remoteAddress!!;
  }

  get remoteFamily(): string {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    return this.#req.socket.remoteFamily!!;
  }

  get remotePort(): number {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    return this.#req.socket.remotePort!!;
  }

  get req(): http.IncomingMessage {
    return this.#req;
  }

  get type(): string | null {
    const contentType = this.headers['content-type'];
    if (!contentType) return null;
    return contentType.split(';')[0];
  }

  getAttr(name: string): AttrValue | undefined {
    return this.#attrs.get(name);
  }

  setAttr(name: string, value: AttrValue): void {
    this.#attrs.set(name, value);
  }

  removeAttr(name: string): AttrValue | undefined {
    if (this.#attrs.has(name)) {
      const value = this.#attrs.get(name);
      this.#attrs.delete(name);
      return value;
    } else {
      return undefined;
    }
  }
}
